<?php

/*
* Класс который умеет создавать объект формы и конфигурировать поля формы в
* соответствии с форматом etc/conf.d/doctype.php. После чего объект формы может делать валидацию и рендеринг.
*
* Нужно знать про поля
* 1) что у каждой формы есть скрытые поля, результат по ним придет через POST флаг "not_render"
* 2) есть служебные поля которые не выводятся но сохраняются в БД для последующих
* выборок по ним. их добавление жестко закодировано.
* 3) есть поля которые нельзя редактировать флаг "not_editable"
* 4) есть поля для которых определена пост-обработка, флаг "external_postprocessing"
*
* Также умеет принимать на входе конфигурацию групп полей. Группы могут быть вложены друг в друга.
* Группа имеет обертку. В основном это и сделано ради обертки.
* Есть архитектурное ограничение - поля типа документа всегда в одной группе, служебные поля могут быть
* разбросаны по разным группам.
*
* @author Alexander Sibiryakov
*/
class FormCreator {
	private $rh;
	private $sections;
	private $store;
	private $doctypes;
	private $default_type;
	private $group_header;

	private $group_config = array(); // заполняется из конфига
	private $group_layout = array();

	private $db_data = NULL;

	public function __construct($rh) {
		$this->rh = $rh;
		$this->store = $rh->ctx->store;
	}

	public function setDoctypes(array $doctypes, $default_type=NULL) {
		$this->doctypes = $doctypes;
		$this->default_type = $default_type;
	}

	public function setSections($sections) {
		$this->sections = $sections;
	}

	public function setSkin($skin) {
		global $_base_dir;

		require_once "{$_base_dir}/skins/{$skin}/create_item_groups.php";
		$this->group_config = $group_config;
		$this->group_header = $group_header;
		$this->group_layout = $group_layout;
	}

	public function setData($db_data) {
		$this->db_data = $db_data;
	}

	public function setParams($section_oid, $section_type, $section_tags, $section_color, $community_oid, $fields, $system_fields) {
		$this->section_oid = $section_oid;
		$this->section_type = $section_type;
		$this->section_tags = $section_tags;
		$this->section_color = $section_color;
		$this->community_oid = $community_oid;
		$this->fields = $fields;
		$this->system_fields = $system_fields;
	}


	private function processFields(&$group, $fields, $community_oid, $section_color, $section_tags, $db_data) {
		require_once 'includes/tags.php';
		foreach ($fields as $field_name=>$field_config)
		if ( !isset($field_config['not_render']) ) { // выполнять содержимое цикла только если поле нужно рендерить, и оно доступно для правки

			if (isset($field_config['options_model'])) { // сейчас происходит разбор поля типа options и в нем указана модель, которую нужно использовать для сбора данных
				$this->rh->UseClass( 'Handler_FormOptionsInterface' );
				$this->rh->UseClass( $field_config['options_model'] );

				$model = new $field_config['options_model']( $this->store );
				if ($model instanceof FormOptionsInterface) {
					$model->init($community_oid);
					$field_config['options'] = $model->getData();
				}

			}

			$tdi_config = array(); // это часть конфига поля уникальная для каждого раздела,
			// $field_config - часть конфига описывающая тип, для любого раздела этого типа

			$tdi_config['interface_tpl_params']['color'] = $section_color; // цвет элементов
			// {{ Tags output
			foreach (tags_split( $section_tags ) as $k=>$v) {
				$tdi_config['interface_tpl_params']['tags'][] = array("tag" => $v);
			}
			// }}

			if ($db_data) {
				if (isset($field_config['not_editable'])) continue;
				$tdi_config['model_default'] = $db_data[ $field_config['db_name'] ]; // структура для загрузки данных в форму
			}

			$config = array_merge( $tdi_config, $field_config ); // итоговый конфиг поля

			if ( 'issue_comments_rights' == $field_name
			|| 'issue_view_rights' == $field_name ) {
				if ( !isset($this->rh->ctx->mm->aa->acl_data['user_id'])
				|| empty($this->rh->ctx->mm->aa->acl_data['user_id']) ) {
					return;
				}
				if ( 'issue_comments_rights' == $field_name ) {
					$selected = $this->getDefaultSelected('comment_create', $config, $db_data['posted_by_id']);
					if ( $selected > 0 ) {
						$config['model_default'] = $selected;
					}
				} else if ( 'issue_view_rights' == $field_name ) {
					$selected = $this->getDefaultSelected('item_read', $config, $db_data['posted_by_id']);
					if ( $selected > 0 ) {
						$config['model_default'] = $selected;
					}
				}
			}

			$group->model->Model_AddField( $field_name, $config );

			unset($config);
		}

	}

	private function walkThruGroupTree(&$parent, $layout) {

		foreach ($layout as $key=>$value) {
			if (is_array($value)) {
				// встретили структуру
				$new_parent = $this->createGroup(&$parent, $key);
				$this->walkThruGroupTree(&$new_parent, $value);
			} else {
				// встретили обычное указание на группу
				// создадим ее в родителе
				$this->createFieldsGroup(&$parent, $value, $this->group_config[$value]);
			}
		}
	}

	private function createGroup(&$parent, $group_name) {
		$group = null;
		$form_config = array(
		"model"     => "group", // все аспекты укладываем в "group"
		"interface" => "group",
		"validator" => "group",
		"view"      => "group",
		"filter"    => "group",

		"group_tpl" => "group:PlainList", // шаблон для вывода группы

		"wrapper"   => "wrapper_group",
		"wrapper_tpl"   => "wrapper:".$group_name,
		"wrapper_params" => array("color"=>$this->section_color, "name"=>$this->section_type, "title"=>$this->doctypes[ $this->section_type ]["title"])
		);

		if ($parent instanceof Form) {
			$group = $parent->AddField( NULL, $form_config );
		} elseif ($parent instanceof FormField) {
			$group = $parent->model->Model_AddField( NULL, $form_config );
		}

		return $group;
	}

	private function createFieldsGroup(&$parent, $group_name, $grp_config) {
		if ((is_array($grp_config)) && (count($grp_config)==0)) {
			return;
		}

		$group = $this->createGroup(&$parent, $group_name);

		if ($grp_config=="DocTypeFields") {
			$this->processFields(&$group, $this->fields, $this->community_oid, $this->section_color, $this->section_tags, $this->db_data);
			return;
		}

		$grp_fields = array();
		foreach ($grp_config as $key) {

			switch ($key) {
				case "doctype":
					$grp_fields[$key] = array(
					"wrapper_title" => "Тип документа",
					"wrapper_tpl" => "wrapper:DocTypeSelectorWrapper",
					"view" => "options",
					"interface" => "options",
					"interface_tpl" => "options:DocTypeSelector",
					"options_mode" => "select",
					"options"      => $this->doctypes,
					"model_default" => $this->default_type,
					"validator_params"=> array(	"not_empty"=>1, ),  // пустое-не пустое?
					);
					break;

				case "tdi_oid":
					$grp_fields[$key] = array(
					"wrapper_title" => "Раздел",
					"wrapper_tpl" => "wrapper:SelectWrapper",
					"view" => "options",
					"interface" => "options",
					"interface_tpl" => "options:Select",
					"options_mode" => "select",
					"options"      => $this->sections,
					"model_default" => $this->db_data[ "tdi_oid" ]
					);
					break;

				case 'tags':
					require_once 'includes/tags.php';
					$grp_fields[$key] = $this->system_fields[$key];
					$model = $this->rh->ctx->mm->getModel("Tdi", "m");
					$sections = $model->getSections(array($this->community_oid), false);

					$parsedTags = array();
					foreach ( $sections as $skey=>$sval ) {
						if ( is_array($sval) && isset($sval['tags']) && !empty($sval['tags']) ) {
							$tags = tags_split($sval['tags']);

							$parsedTags[] = array( 'tdi_oid'=>$skey, 'tags'=>$tags );
						}
					}
					if ( !empty($parsedTags) ) {
						$grp_fields[$key]['interface_tpl_params']['ptags'] = $parsedTags;
					}
					break;

				default:
					$grp_fields[$key] = $this->system_fields[$key];
			}

		}

		$this->processFields(&$group, $grp_fields, $this->community_oid, $this->section_color, $this->section_tags, $this->db_data);

	}

	/**
	 * Создает объект класса Form и подготавливает его к рендерингу/валидации используя конфиг
	 *
	 * @param $fields - конфиг из doctype.php и та часть которая описывает именно поля (пр $self['review']['fields']),
	 * $db_data - хэш с данными
	 * @return Form
	 **/
	public function createForm() {
		$form = &new Form( &$this->rh,
		array(
		"auto_user_id"  => 0,
		"template_form"	=> "form:Form",
		"template_buttonlist" => "form:Buttons",
		"db_table" => $this->section_oid, // это id формы
		), NULL,
		NULL // дефолтные шаблонные переменные, если таковые есть в шаблонах
		);

		$this->walkThruGroupTree($form, $this->group_layout);

		$field_text = $form->AddField( "objtype", array(
		"wrapper_tpl"   => "wrapper:Plain", // задаём только враппер
		"model_default" => $this->section_type,
		"interface_tpl" => "string:Hidden",
		"interface_tpl_params" => array( // дополнительные параметры чисто для html-шаблона
		"id" => "objtype",
		),
		) );


		// у каждой формы две кнопки
		$form->AddButton( array(
		"event" => FORM_EVENT_OK,
		"title" => "Okay",
		"template" => "Submit",
		));

		/*
		* HACK : 2008-06-02 : lucky
		* Не понятно что должна делать эта кнопка.
		$form->AddButton( array(
		"event" => FORM_EVENT_CANCEL,
		"title" => "Cancel",
		"template" => "Cancel",
		));
		*/
		return $form;
	}

	private function getDefaultSelected( $rightName, $config, $poster ) {
		$aa = $this->rh->ctx->mm->aa;

		$oid = 0;
		if ( isset($aa->acl_data['id_item']) && !empty($aa->acl_data['id_item']) ) {
			$oid = $aa->acl_data['id_item'];
		} else if ( isset($aa->acl_data['tdi']) && !empty($aa->acl_data['tdi'])
		&& isset($aa->acl_data['tdi']->id) && !empty($aa->acl_data['tdi']->id) ) {
			$oid = $aa->acl_data['tdi']->id;
		} else if ( isset($aa->acl_data['comity_oid']) && !empty($aa->acl_data['comity_oid']) ) {
			$oid = $aa->acl_data['comity_oid'];
		}

		if ( $oid > 0 && isset($config['test_roles']) && !empty($config['test_roles']) ) {
			$selected_oid = 0;

			$rolesList = $aa->getRawRolesList();
			$rolesList['poster'] = $poster;

			foreach( $config['test_roles'] as $role=>$selected ) {
				$role_oid = 0;
				if ( isset($rolesList[$role]) && !empty($rolesList[$role]) ) {
					$role_oid = $rolesList[$role];
				}

				if ( $role_oid > 0 ) {
					$acl = $aa->getAclbyRole($oid, $role_oid);

					if ( !empty($acl) && isset($acl[$rightName])
					&& isset($acl[$rightName]['is_allowed'])
					&& $acl[$rightName]['is_allowed'] ) {
						$selected_oid = $selected;
						break;
					}
				}
			}

			return $selected;
		}

		return 0;
	}
}
